#include "rotary_encoder.h"
#include "system_sm.h"
#include "sh_button.h"
#include "main.h"
#include "gpio.h"

static sh_button_ctrl_t sh_button_ctrl;
static sh_button_t sh_button;

static uint8_t sh_get_button_level(uint8_t id)
{
    return HAL_GPIO_ReadPin(OK_GPIO_Port, OK_Pin);
}

static void board_button_cb_fn(sh_button_t *button, enum sh_button_event event)
{
    switch (event) {
    case SH_BUTTON_EVENT_SINGLE_CLICK:
        system_sm_send_event(EVENT_BUTTON_OK);
        break;

    case SH_BUTTON_EVENT_DOUBLE_CLICK:
        system_sm_send_event(EVENT_BUTTON_BACK);
        break;

    default:
        break;
    }
}

void rotary_encoder_init(void)
{
    sh_button_config_t ok_config = {
        .name               = "ok",
        .id                 = 0,
        .active_level       = SH_BUTTON_ACTIVE_LOW,
        .get_button_level   = sh_get_button_level,
    };

    sh_button_init(&sh_button, &ok_config);
    sh_button_attach_cbs(&sh_button, board_button_cb_fn);

    sh_button_ctrl_init(&sh_button_ctrl);
    sh_button_ctrl_add(&sh_button_ctrl, &sh_button);
}

struct rotary_encoder_aa_info {
    uint8_t input_val;
    uint8_t input_cnt;
    uint8_t input_delay;
};

struct rotary_encoder_aa_info aa_info = {
    .input_val = 0,
    .input_cnt = 0,
    .input_delay = 5,
};

struct rotary_encoder_aa_info bb_info = {
    .input_val = 0,
    .input_cnt = 0,
    .input_delay = 5,
};

void rotary_encoder_button_detect(void)
{
    GPIO_PinState bit_temp = HAL_GPIO_ReadPin(AA_GPIO_Port, AA_Pin);
    if(bit_temp == GPIO_PIN_SET)
    {
        if(aa_info.input_cnt < aa_info.input_delay)
        {
            aa_info.input_cnt++;
            if(aa_info.input_cnt == aa_info.input_delay)
            {
                aa_info.input_val = 1;
            }
        }
    }
    else if(bit_temp == GPIO_PIN_RESET)
    {
        if(aa_info.input_cnt > 0)
        {
            aa_info.input_cnt--;
            if(aa_info.input_cnt == 0)
            {
                aa_info.input_val = 0;
            }
        }
    }

    bit_temp = HAL_GPIO_ReadPin(BB_GPIO_Port, BB_Pin);
    if(bit_temp == GPIO_PIN_SET)
    {
        if(bb_info.input_cnt < bb_info.input_delay)
        {
            bb_info.input_cnt++;
            if(bb_info.input_cnt == bb_info.input_delay)
            {
                bb_info.input_val = 1;
            }
        }
    }
    else if(bit_temp == GPIO_PIN_RESET)
    {
        if(bb_info.input_cnt > 0)
        {
            bb_info.input_cnt--;
            if(bb_info.input_cnt == 0)
            {
                bb_info.input_val = 0;
            }
        }
    }
}

void rotary_encoder_button_timer_cb(void *param)
{
    sh_button_handler(&sh_button_ctrl);
    rotary_encoder_button_detect();
}

struct rotary_encoder_tick_ctrl {
    uint32_t current_tick;
    uint8_t current_event;
    uint32_t last_tick;
    uint8_t last_event;
    uint8_t short_gap_cnt;
};

struct rotary_encoder_tick_ctrl tick_ctrl = {0};

void rotary_encoder_detect(uint8_t input_val)
{
    uint32_t add = 1;

    tick_ctrl.current_tick = HAL_GetTick();

    uint32_t gap = tick_ctrl.current_tick - tick_ctrl.last_tick;
    if (gap < 60) {
        tick_ctrl.short_gap_cnt++;
    } else if (gap > 300) {
        tick_ctrl.short_gap_cnt = 0;
    } else {
        if (tick_ctrl.short_gap_cnt > 0) {
            tick_ctrl.short_gap_cnt--;
        }
    }

    tick_ctrl.last_tick = tick_ctrl.current_tick;

    if (tick_ctrl.short_gap_cnt >= 3) {
        add = tick_ctrl.short_gap_cnt * 2;
    }

    if (input_val == 0) {
        tick_ctrl.current_event = EVENT_BUTTON_UP;
    } else {
        tick_ctrl.current_event = EVENT_BUTTON_DOWN;
    }

    if (tick_ctrl.current_event != tick_ctrl.last_event) {
        if (gap < 80) {
            tick_ctrl.current_event = tick_ctrl.last_event;
            return;
        }
    }

    system_sm_send_event_with_param(tick_ctrl.current_event, add);
    tick_ctrl.last_event = tick_ctrl.current_event;
}

void rotary_encoder_AA_called_in_exti_int(void)
{
    rotary_encoder_detect(bb_info.input_val);
}

void rotary_encoder_BB_called_in_exti_int(void)
{
    rotary_encoder_detect(aa_info.input_val);
}

void rotary_encoder_exti_irq_ctrl(FunctionalState state)
{
    if (state == ENABLE) {
        HAL_NVIC_EnableIRQ(AA_EXTI_IRQn);
        HAL_NVIC_EnableIRQ(BB_EXTI_IRQn);
    } else {
        HAL_NVIC_DisableIRQ(AA_EXTI_IRQn);
        HAL_NVIC_DisableIRQ(BB_EXTI_IRQn);
    }
}


